package per.hnvcam.util.rws.services.impl;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Service;

import per.hnvcam.util.rws.ResourceMetadata;
import per.hnvcam.util.rws.ResourceWatchingEvent;
import per.hnvcam.util.rws.ResourceWatchingListener;
import per.hnvcam.util.rws.services.ResourceWatcher;

@Service("resourceWatcher")
public class ResourceWatcherImpl implements ResourceWatcher {

   private Map<String, ResourceMetadata> watchedResources;

   private List<ResourceWatchingListener> listeners;

   public ResourceWatcherImpl() {
      this.watchedResources = new HashMap<String, ResourceMetadata>();
      this.listeners = new LinkedList<ResourceWatchingListener>();
   }

   public void addListener(ResourceWatchingListener listener) {
      this.listeners.add(listener);
   }

   public void removeListener(ResourceWatchingListener listener) {
      this.listeners.remove(listener);
   }

   public List<ResourceWatchingListener> getListeners() {
      return this.listeners;
   }

   public void addResource(String resource) {
      watchedResources
            .put(resource, ResourceMetadata.getMetadataFrom(resource));
   }

   public ResourceMetadata removeResource(String resource) {
      return watchedResources.remove(resource);
   }

   public void scanForChanges() {
      ResourceWatchingEvent event = new ResourceWatchingEvent();
      for (String resource : this.watchedResources.keySet()) {
         ResourceMetadata newMetadata = ResourceMetadata
               .getMetadataFrom(resource);
         ResourceMetadata existingMetadata = this.watchedResources
               .get(resource);
         int eventCode = calculateEvent(existingMetadata, newMetadata);
         if (eventCode != ResourceWatchingEvent.UNCHANGED) {
            event.addResourceEvent(resource, eventCode);
         }
      }
      if (event.getResources().size() > 0) {
         for (ResourceWatchingListener listener : this.listeners) {
            listener.handleEvent(event);
         }
      }
   }

   private int calculateEvent(ResourceMetadata existingMetadata,
         ResourceMetadata newMetadata) {
      int eventCode = ResourceWatchingEvent.UNCHANGED;
      if (existingMetadata.lastModified() < newMetadata.lastModified()) {
         if (existingMetadata.getStatus() == ResourceMetadata.EXISTING) {
            if (newMetadata.getStatus() == ResourceMetadata.EXISTING) {
               eventCode = ResourceWatchingEvent.MODIFIED;
            } else {
               eventCode = ResourceWatchingEvent.REMOVED;
            }
         } else {
            if (newMetadata.getStatus() == ResourceMetadata.EXISTING) {
               eventCode = ResourceWatchingEvent.CREATED;
            }
         }
      } else {
         if (existingMetadata.canExecute() != newMetadata.canExecute()
               || existingMetadata.canRead() != newMetadata.canRead()
               || existingMetadata.canWrite() != newMetadata.canWrite()) {
            eventCode = ResourceWatchingEvent.ACCESSIBILITY_CHANGED;
         }
      }

      return eventCode;
   }
}
